27. Return behavior

Note

The below information is extensively based in information taken from the PowerShell® Notes for Professionals book. I plan to extend this information based on my day to day usage of the language.

It can be used to Exit the current scope, which can be a function, script, or script block. In PowerShell, the result of each statement is returned as output, even without an explicit Return keyword or to indicate that the end of the scope has been reached.

27.1: Early exit

1
2
3
4
5
function earlyexit {
  "Hello"
  return
  "World"
}

"Hello" will be placed in the output pipeline, "World" will not

27.2: Gotcha! Return in the pipeline

1
get-childitem | foreach-object { if ($_.IsReadOnly) { return } }

Pipeline cmdlets (ex: ForEach-Object , Where-Object , etc) operate on closures. The return here will only move to the next item on the pipeline, not exit processing. You can use break instead of return if you want to exit processing.

1
get-childitem | foreach-object { if ($_.IsReadOnly) { break } }

27.3: Return with a value

The following methods will have the same values on the pipeline

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function foo {
  $a = "Hello"
  return $a
}

function bar {
  $a = "Hello"
  $a
  return
}

function quux {
  $a = "Hello"
  $a
}

27.4: How to work with functions returns

A function returns everything that is not captured by something else.

If u use the return keyword, every statement after the return line will not be executed!

Like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Function Test-Function
{
  Param
  (
    [switch]$ExceptionalReturn
  )
  "Start"
  if($ExceptionalReturn){Return "Damn, it didn't work!"}
  New-ItemProperty -Path "HKCU:\" -Name "test" -Value "TestValue" - Type "String"
  Return "Yes, it worked!"
}

Test-Function

Will return:

1
2
3
- Start
- The newly created registry key (this is because there are some statements that create output that you may not be expecting)
- Yes, it worked!

Test-Function -ExceptionalReturn Will return:

  • Start
  • Damn, it didn't work!

If you do it like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Function Test-Function
{
  Param
  (
    [switch]$ExceptionalReturn
  )
  . {
    "Start"
    if($ExceptionalReturn)
    {
      $Return = "Damn, it didn't work!"
      Return
    }
    **New-ItemProperty** _-Path_ "HKCU:\" _-Name_ "test" _-Value_ "TestValue" - **Type** "String"
    $Return = "Yes, it worked!"
    Return
  } | **Out-Null**
  Return $Return
}

Test-Function

Will return:

  • Yes, it worked!

Test-Function -ExceptionalReturn Will return:

  • Damn, it didn't work!

With this trick you can control the returned output even if you are not sure what will each statement will spit out. It works like this

1
.{<Statements>} | Out-Null

the. makes the following scriptblock included in the code

the {} marks the script block

the | Out-Null pipes any unexpected output to Out-Null (so it is gone!)

Because the scriptblock is included it gets the same scope as the rest of the function.

So you can access variables who were made inside the scriptblock.

27.5: Gotcha! Ignoring unwanted output

1
2
3
4
5
6
function bar {
  [System.Collections.ArrayList]$MyVariable = @()
  $MyVariable.Add("a") | Out-Null
  $MyVariable.Add("b") | Out-Null
  $MyVariable
}

The Out-Null is necessary because the .NET ArrayList.Add method returns the number of items in the collection after adding. If omitted, the pipeline would have contained 1 , 2 , "a", "b"

There are multiple ways to omit unwanted output:

1
2
3
4
5
6
7
8
function bar
{
  # New-Item cmdlet returns information about newly created file/folder
  New-Item "test1.txt" | out-null
  New-Item "test2.txt" > $null
  [void]( New-Item "test3.txt")
  $tmp = New-Item "test4.txt"
}

Note: to learn more about why to prefer > $null, see [topic not yet created].